package net.goutalk.glcs.module.organization.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import net.goutalk.glcs.common.annotation.GlcsLog;
import net.goutalk.glcs.common.constant.GlobalConstant;
import net.goutalk.glcs.common.enums.YesOrNoEnum;
import net.goutalk.glcs.common.page.ConventPage;
import net.goutalk.glcs.common.page.PageOutput;
import net.goutalk.glcs.common.model.result.R;
import net.goutalk.glcs.common.utils.RedisUtil;
import net.goutalk.glcs.common.utils.TreeUtil;
import net.goutalk.glcs.common.utils.VoToColumnUtil;
import net.goutalk.glcs.module.organization.dto.*;
import net.goutalk.glcs.module.organization.entity.Department;
import net.goutalk.glcs.module.organization.entity.User;
import net.goutalk.glcs.module.organization.entity.UserDeptRelation;
import net.goutalk.glcs.module.organization.service.IDepartmentService;
import net.goutalk.glcs.module.organization.service.IUserDeptRelationService;
import net.goutalk.glcs.module.organization.service.IUserService;
import net.goutalk.glcs.module.organization.vo.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 机构 前端控制器
 * </p>
 *
 * @author tanyujie
 * @since 2022-03-02
 */
@RestController
@RequestMapping(GlobalConstant.ORGANIZATION_MODULE_PREFIX + "/department")
@Api(value = GlobalConstant.ORGANIZATION_MODULE_PREFIX + "/department", tags = "机构")
@AllArgsConstructor
public class DepartmentController {

    private final IDepartmentService departmentService;

    private final IUserService userService;

    private final IUserDeptRelationService userDeptRelationService;

    private final RedisUtil redisUtil;

    @GetMapping(value = "/list")
    @ApiOperation(value = "机构列表(不分页)")
    @GlcsLog(value = "获取不分页机构列表")
    public R list() {
        List<Department> list = departmentService.list(Wrappers.lambdaQuery(Department.class)
                .select(Department.class, x -> VoToColumnUtil.fieldsToColumns(DepartmentListVo.class).contains(x.getProperty())));

        List<DepartmentListVo> departmentListVos = BeanUtil.copyToList(list, DepartmentListVo.class);
        return R.ok(departmentListVos);
    }

    @GetMapping(value = "/page")
    @ApiOperation(value = "机构列表(分页)")
    @GlcsLog(value = "获取分页机构列表")
    public R page(DepartmentPageDto dto) {

        LambdaQueryWrapper<Department> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StrUtil.isNotBlank(dto.getKeyword()), Department::getName, dto.getKeyword())
                .like(StrUtil.isNotBlank(dto.getKeyword()), Department::getCode, dto.getKeyword())
                .like(StrUtil.isNotBlank(dto.getName()), Department::getName, dto.getName())
                .like(StrUtil.isNotBlank(dto.getCode()), Department::getCode, dto.getCode())
                .like(ObjectUtil.isNotEmpty(dto.getEnabledMark()), Department::getEnabledMark, dto.getEnabledMark())
                .select(Department.class, x -> VoToColumnUtil.fieldsToColumns(DepartmentPageVo.class).contains(x.getProperty()));

        IPage<Department> page = departmentService.page(ConventPage.getPage(dto), queryWrapper);
        PageOutput<DepartmentPageVo> pageOutput = ConventPage.getPageOutput(page, DepartmentPageVo.class);
        return R.ok(pageOutput);
    }

    @GetMapping(value = "/enabled-tree")
    @ApiOperation(value = "机构树")
    @GlcsLog(value = "部门展示 所有数据")
    public R tree(DepartmentTreeDto dto) {

        List<Department> list = departmentService.list(Wrappers.<Department>lambdaQuery()
                .like(StrUtil.isNotBlank(dto.getName()), Department::getName, dto.getName())
                .like(StrUtil.isNotBlank(dto.getCode()), Department::getCode, dto.getCode())
                .like(ObjectUtil.isNotEmpty(dto.getEnabledMark()), Department::getEnabledMark, dto.getEnabledMark())
                .select(Department.class, x -> VoToColumnUtil.fieldsToColumns(DepartmentTreeVo.class).contains(x.getProperty())));

        List<DepartmentTreeVo> voList = BeanUtil.copyToList(list, DepartmentTreeVo.class);
        List<DepartmentTreeVo> treeVoList = TreeUtil.build(voList);
        return R.ok(treeVoList);
    }

    @GetMapping(value = "/tree")
    @ApiOperation(value = "部门展示 (忽略 未启用的数据)")
    @GlcsLog(value = "部门展示 (忽略 未启用的数据)")
    public R treeEnabled(DepartmentTreeDto dto) {
        List<Department> list = departmentService.list(Wrappers.<Department>lambdaQuery()
                .like(StrUtil.isNotBlank(dto.getName()), Department::getName, dto.getName())
                .like(StrUtil.isNotBlank(dto.getCode()), Department::getCode, dto.getCode())
                .like(ObjectUtil.isNotEmpty(dto.getEnabledMark()), Department::getEnabledMark, dto.getEnabledMark())
                .select(Department.class, x -> VoToColumnUtil.fieldsToColumns(DepartmentTreeVo.class).contains(x.getProperty())));

        List<DepartmentTreeVo> voList = BeanUtil.copyToList(list, DepartmentTreeVo.class);

        //所有需要过滤掉下级节点的deptIds
        List<Long> deptIds = new ArrayList<>();
        for (DepartmentTreeVo departmentTreeVo : voList) {
            if (departmentTreeVo.getEnabledMark() == YesOrNoEnum.NO.getCode()) {
                //如果有下级，下级进行过滤，不显示到前端去
                deptIds.add(departmentTreeVo.getId());
                departmentTreeVo.setDisabled(true);
            } else {
                departmentTreeVo.setDisabled(false);
            }
        }

        //所有需要过滤的ids的集合
        List<Long> allChildIds = departmentService.getAllChildIds(deptIds, voList);

        //不是组织管理模块请求过滤父级,如果是组织管理isOrg不为空,不用过滤
        if (ObjectUtil.isEmpty(dto.getIsOrg())) {
            //将父级节点也过滤掉
            allChildIds.addAll(deptIds);
        }

        List<DepartmentTreeVo> vo = voList.stream().filter(u -> !allChildIds.contains(u.getId())).collect(Collectors.toList());

        List<DepartmentTreeVo> treeVoList = TreeUtil.build(vo);
        for (DepartmentTreeVo departmentTreeVo : treeVoList) {
            if (departmentTreeVo.getParentId() == 0) {
                departmentTreeVo.setParentId(null);
            }
        }
        return R.ok(treeVoList);
    }

    @GetMapping(value = "/info")
    @ApiOperation(value = "根据id查询机构信息")
    @GlcsLog(value = "id查询获取机构信息")
    public R info(@RequestParam Long id) {
        Department dept = departmentService.getById(id);
        if (dept == null) {
            R.error("找不到此机构！");
        }
        return R.ok(BeanUtil.toBean(dept, DepartmentVo.class));
    }

    @PostMapping
    @ApiOperation(value = "新增机构")
    @GlcsLog(value = "新增机构")
    public R add(@Valid @RequestBody AddDepartmentDto dto) {
        long count = departmentService.count(Wrappers.<Department>query().lambda().eq(Department::getName, dto.getName()).or().eq(Department::getCode, dto.getCode()));
        if (count > 0) {
            return R.error("机构名称与机构编码已经存在！");
        }
        Department department = BeanUtil.toBean(dto, Department.class);
        if (ObjectUtil.isEmpty(dto.getParentId())) {
            department.setParentId(0L);
        }
        departmentService.save(department);
        //设置层级
        if (ObjectUtil.isNotEmpty(dto.getParentId())){
            List<Department> list = redisUtil.get(GlobalConstant.DEP_CACHE_KEY,new TypeReference<List<Department>>() {
            });
            //获取父级的层级
            String hierarchy = list.stream().filter(x->x.getId().equals(dto.getParentId())).findFirst().orElse(new Department()).getHierarchy();
            //在父级的基础上添加层级
            department.setHierarchy(hierarchy + StringPool.DASH + department.getId());
        }else {
            department.setHierarchy(department.getId().toString());
        }
        departmentService.updateById(department);
        CompletableFuture.runAsync(() -> {
            List<Department> list = departmentService.list();
            redisUtil.set(GlobalConstant.DEP_CACHE_KEY, list);
        });
        return R.ok(true);
    }

    @PutMapping
    @ApiOperation(value = "修改机构")
    @GlcsLog(value = "修改机构")
    public R update(@Valid @RequestBody UpdateDepartmentDto dto) {

        long count = departmentService.count(Wrappers.<Department>query().lambda()
                .eq(Department::getName, dto.getName())
                .ne(Department::getId, dto.getId())
                .or()
                .eq(Department::getCode, dto.getCode())
                .ne(Department::getId, dto.getId()));

        if (count > 0) {
            return R.error("机构名称与机构编码已经存在！");
        }

        Department department = BeanUtil.toBean(dto, Department.class);
        if (department.getParentId() == null) {
            department.setParentId(0L);
        }
        departmentService.updateById(department);

        CompletableFuture.runAsync(() -> {
            List<Department> list = departmentService.list();
            redisUtil.set(GlobalConstant.DEP_CACHE_KEY, list);
        });

        return R.ok(true);
    }

    @DeleteMapping
    @ApiOperation(value = "删除")
    @GlcsLog(value = "删除机构")
    public R delete(@RequestBody List<Long> ids) {
        //删除岗位时，需要判断，此机构下是不是存在人员，存在人员就不能删除
        List<UserDeptRelation> userDeptRelationList = redisUtil.get(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, new TypeReference<List<UserDeptRelation>>() {
        });
        //拿ids进行过滤，如果存在，就不能删除
        List<UserDeptRelation> users = userDeptRelationList.stream().filter(u -> ids.contains(u.getDeptId())).collect(Collectors.toList());
        if (users.size()>0){
            return R.error("此机构下存在用户！");
        }
        departmentService.removeBatchByIds(ids);
        CompletableFuture.runAsync(() -> {
            List<Department> list = departmentService.list();
            redisUtil.set(GlobalConstant.DEP_CACHE_KEY, list);
        });
        return R.ok(true);
    }

    @PostMapping("add-dept-user")
    @ApiOperation(value = "添加人员（组织）")
    @Transactional(rollbackFor = Exception.class)
    public R addDeptUser(@Valid @RequestBody AddDepartmentUserDto dto) {
        //先删除再新增
        userDeptRelationService.remove(Wrappers.<UserDeptRelation>query().lambda().eq(UserDeptRelation::getDeptId,dto.getId()));
        List<UserDeptRelation> userDeptRelationList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(dto.getUserIds())){
            for (Long userId : dto.getUserIds()) {
                //将用户所选部门保存到关联表中
                UserDeptRelation userDeptRelation = new UserDeptRelation();
                userDeptRelation.setUserId(userId);
                userDeptRelation.setDeptId(dto.getId());
                userDeptRelationList.add(userDeptRelation);
            }
        }
        userDeptRelationService.saveBatch(userDeptRelationList);
        //更新缓存
        CompletableFuture.runAsync(() -> {
            List<UserDeptRelation> deptRelationList = userDeptRelationService.list(Wrappers.lambdaQuery(UserDeptRelation.class));
            redisUtil.set(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, deptRelationList);
        });
        return R.ok(true);
    }

    @GetMapping(value = "/dept-user-info")
    @ApiOperation(value = "根据组织id查询组织下的人员")
    public R DeptUserInfo(@RequestParam Long id) {
        List<Long> userIdList = userDeptRelationService.list(Wrappers.<UserDeptRelation>query().lambda().eq(UserDeptRelation::getDeptId, id)).stream().map(UserDeptRelation::getUserId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(userIdList)) {
            return R.ok(new ArrayList<>());
        }
        List<User> users = userService.list(Wrappers.<User>query().lambda().in(User::getId, userIdList).select(User.class, x -> VoToColumnUtil.fieldsToColumns(UserRoleVo.class).contains(x.getProperty())));
        List<UserRoleVo> userRoleVos = BeanUtil.copyToList(users, UserRoleVo.class);
        return R.ok(userRoleVos);
    }

}
